<template>
  <svg
    ref="filStatusSvg"
    viewBox="140 20 285 421"
    preserveAspectRatio="xMidYMid meet"
    class="svg-colors"
  >
    <defs>
      <g
        id="sync-feedback"
        style="stroke: var(--color-outline); stroke-linecap: round; stroke-linejoin: round; fill: none;"
      >
        <path
          d="M18,9,13.78,3.39a1,1,0,0,0-1.56,0L8,9"
          style="stroke-width: 1; stroke-opacity: 0.8"
        />
        <path
          d="M13,8.24,18,15H15H8Z"
          style="stroke-width: 2"
        />
      </g>

      <g
        id="sissors"
        style="stroke: var(--color-outline); fill: none; stroke-linecap: round; stroke-linejoin: round;"
      >
        <path
          d="M8.8,7.72c-.6,1.21-2.34,1.64-3.89,1S2.6,6.48,3.2,5.28s2.34-1.64,3.89-1S9.4,6.52,8.8,7.72Zm-3.89,1L21,16M7.09,19.68c-1.55.68-3.29.25-3.89-1s.17-2.73,1.71-3.4,3.29-.25,3.89,1S8.63,19,7.09,19.68ZM21,8,4.91,15.32"
          style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2"
        />
        <line
          x1="31"
          y1="12.5"
          x2="44"
          y2="12.5"
          style="stroke-width: 1; stroke-dasharray: 2, 2"
        />
      </g>

      <g id="sync-extruder">
        <g
          style="
                        stroke: none;
                        stroke-width: 2;
                        stroke-dasharray: none;
                        stroke-linecap: round;
                        stroke-linejoin: round;
                        stroke-miterlimit: 4;
                    "
        >
          <rect
            style="fill: rgb(131, 148, 150)"
            x="145"
            y="506"
            width="710"
            height="256"
          />
          <rect
            style="fill: rgb(147, 161, 161)"
            x="324"
            y="506"
            width="355"
            height="256"
          />
          <rect
            style="fill: rgb(101, 123, 131)"
            x="274"
            y="364"
            width="455"
            height="142"
          />
          <rect
            style="fill: rgb(88, 110, 117)"
            x="181"
            y="222"
            rx="28"
            ry="28"
            width="639"
            height="142"
          />
          <rect
            style="fill: rgb(88, 110, 117)"
            x="181"
            y="108"
            width="639"
            height="142"
          />
          <path
            style="fill: rgb(88, 110, 117)"
            d="m 322 762 h 355 l -118 142 h -118 z"
          />
        </g>
        <g transform="matrix(23.2058 0 0 23.2058 329.7195 325.9517)">
          <path
            style="
                            stroke: rgb(0, 0, 0);
                            stroke-width: 0;
                            stroke-dasharray: none;
                            stroke-linecap: butt;
                            stroke-dashoffset: 0;
                            stroke-linejoin: miter;
                            stroke-miterlimit: 4;
                            is-custom-font: none;
                            font-file-url: none;
                            fill: rgb(254, 162, 54);
                            fill-rule: nonzero;
                            opacity: 1;
                        "
            vector-effect="non-scaling-stroke"
            transform=" translate(-15.4288, -16.4198)"
            stroke-linecap="round"
            d="M 25.032 26.16 c 2.884 -2.883 4.184 -6.74 3.928 -10.51 c -1.511 0.013 -3.021 0.021 -4.531 0.034 c 0.254 2.599 -0.603 5.287 -2.594 7.277 c -3.535 3.533 -9.263 3.533 -12.796 0 c -3.534 -3.533 -3.534 -9.26 0 -12.794 c 3.015 -3.016 7.625 -3.446 11.109 -1.314 c -1.181 1.167 -2.57 2.549 -2.57 2.549 c -1 1.062 0.016 1.766 0.69 1.77 h 8.828 c 0.338 0 0.611 -0.274 0.612 -0.612 V 3.804 c 0.041 -0.825 -0.865 -1.591 -1.756 -0.7 c 0 0 -1.495 1.48 -2.533 2.509 C 18.112 1.736 10.634 2.175 5.841 6.967 c -5.3 5.3 -5.3 13.892 0 19.193 C 11.141 31.459 19.733 31.459 25.032 26.16 z"
          />
        </g>
      </g>
      <g
        id="blob"
        style="stroke-linecap: round; stroke-linejoin: round"
      >
        <polygon
          points="0,0 -1,1 1,1 0,0"
          stroke-width="4"
          vector-effect="non-scaling-stroke"
        />
        <polygon
          points="-0.1,0.3 -0.5,0.7 0.3,0.7 -0.1,0.3"
          stroke-width="1"
          stroke="white"
          fill="white"
          opacity="0.3"
          vector-effect="non-scaling-stroke"
        />
      </g>
    </defs>

    <rect
      x="150"
      y="30"
      width="265"
      height="130"
      :class="$vuetify.theme.dark ? 'zone-background-dark-theme' : 'zone-background-light-theme'"
      rx="10"
      ry="10"
    />
    <rect
      x="150"
      y="333"
      width="265"
      height="66"
      :class="$vuetify.theme.dark ? 'zone-background-dark-theme' : 'zone-background-light-theme'"
      rx="10"
      ry="10"
    />
    <g style="stroke: var(--color-outline); stroke-linecap: round; stroke-linejoin: round; stroke-width: 1;">
      <path
        d="M242 25 L242 405 L249 411 L251 411 L258 405 L258 25"
        style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 1"
      />
    </g>

    <rect
      ref="filamentRect"
      x="243"
      y="25"
      width="14"
      :height="filamentRectHeight"
      :fill="currentGateColor"
      :class="tipFormingClass"
    />
    <polygon
      v-if="upperNozzleFull"
      points="257,380 243,380 243,396 257,396"
      :fill="upperNozzleColor"
    />
    <polygon
      v-if="lowerNozzleFull"
      points="257,396 243,396 243,405 249,412 249,413 251,413 251,412 257,405"
      :fill="lowerNozzleColor"
    />

    <g
      style="stroke: var(--color-outline); stroke-linejoin: round; stroke-width: 0; font-family: Roboto; font-size: 16;"
    >
      <g v-if="hasSensor('mmu_pre_gate')">
        <circle
          cx="258"
          cy="50"
          r="8"
          style="stroke-width: 1"
          :class="sensorClass('mmu_pre_gate')"
        />
        <text
          x="278"
          y="55"
          :class="{ 'text-disabled': !isSensorEnabled('mmu_pre_gate') }"
        >Pre-Gate</text>
      </g>

      <g v-if="hasSensor('mmu_gear')">
        <circle
          cx="258"
          cy="80"
          r="8"
          style="stroke-width: 1"
          :class="sensorClass('mmu_gear')"
        />
        <text
          x="278"
          y="85"
          :class="{ 'text-disabled': !isSensorEnabled('mmu_gear') }"
        >Gear</text>
        <text
          v-if="homedToGear"
          x="219.5"
          y="85"
          font-weight="bold"
        >H</text>
      </g>

      <g v-if="hasSensor('mmu_gate')">
        <circle
          cx="258"
          cy="110"
          r="8"
          style="stroke-width: 1"
          :class="sensorClass('mmu_gate')"
        />
        <text
          x="278"
          y="115"
          :class="{ 'text-disabled': !isSensorEnabled('mmu_gate') }"
        >Gate</text>
        <transition name="fade">
          <text
            v-if="homedToGate"
            x="219.5"
            y="115"
            font-weight="bold"
          >H</text>
        </transition>
      </g>

      <g v-if="hasEncoder">
        <circle
          cx="258"
          cy="140"
          r="8"
          style="stroke-width: 1"
          :class="encoderClass"
        />
        <path
          d="M257 135 L261 140 L257 145"
          stroke-width="2"
          fill="none"
        />
        <text
          x="278"
          y="145"
        >Encoder</text>
        <text
          x="345"
          y="145"
          font-size="11px"
        >{{ encoderPosText }}</text>
        <transition name="fade">
          <text
            v-if="homedToEncoder"
            x="219.5"
            y="145"
            font-weight="bold"
          >H</text>
        </transition>
      </g>

      <g v-if="hasSensor('extruder')">
        <circle
          cx="258"
          cy="320"
          r="8"
          style="stroke-width: 1"
          :class="sensorClass('extruder')"
        />
        <text
          x="278"
          y="325"
          :class="{ 'text-disabled': !isSensorEnabled('extruder') }"
        >Extruder</text>
        <transition name="fade">
          <text
            v-if="homedToExtruder"
            x="219.5"
            y="325"
            font-weight="bold"
          >H</text>
        </transition>
      </g>

      <transition name="fade">
        <text
          v-if="homedToExtruderEntrance"
          x="219.5"
          y="339"
          font-weight="bold"
        >H</text>
      </transition>

      <g v-if="hasSensor('toolhead')">
        <circle
          cx="258"
          cy="350"
          r="8"
          style="stroke-width: 1"
          :class="sensorClass('toolhead')"
        />
        <text
          x="278"
          y="355"
          :class="{ 'text-disabled': !isSensorEnabled('toolhead') }"
        >Toolhead</text>
        <transition name="fade">
          <text
            v-if="homedToToolhead"
            x="219.5"
            y="355"
            font-weight="bold"
          >H</text>
        </transition>
      </g>

      <text
        x="228"
        y="412"
        font-size="11px"
        font-weight="bold"
        text-anchor="end"
        :class="temperatureClass"
      >
        {{ temperatureText }}
      </text>

      <g v-if="hasSyncFeedback">
        <transition name="fade">
          <g
            v-if="isSensorTriggered('filament_tension')"
            key="tension"
          >
            <use
              xlink:href="#sync-feedback"
              transform="translate(258, 199) scale(1.2)"
            />
            <use
              xlink:href="#sync-feedback"
              transform="translate(258, 271) scale(1.2,-1.2)"
            />
          </g>
        </transition>
        <transition name="fade">
          <g
            v-if="isSensorTriggered('filament_compression')"
            key="compression"
          >
            <use
              xlink:href="#sync-feedback"
              transform="translate(258, 235) scale(1.2)"
            />
            <use
              xlink:href="#sync-feedback"
              transform="translate(258, 235) scale(1.2,-1.2)"
            />
          </g>
        </transition>
        <transition name="fade">
          <g
            v-if="isSensorTriggered('filament_tension') || isSensorTriggered('filament_compression')"
            key="feedback"
          >
            style="font-size: 14px"
            <text
              v-if="isSensorTriggered('filament_tension') && isSensorTriggered('filament_compression')"
              x="288"
              y="240"
              fill="#FF0000"
            >
              Error!
            </text>
            <text
              v-else-if="isSensorTriggered('filament_tension')"
              x="288"
              y="240"
            >Tension</text>
            <text
              v-else-if="isSensorTriggered('filament_compression')"
              x="288"
              y="240"
            >Compression</text>
          </g>
        </transition>
      </g>
      <text
        x="160"
        y="60"
        :class="tool === -2 ? 'tool-bypass' : 'tool-text'"
      >{{ toolText(tool) }}</text>
    </g>

    <transition name="fade">
      <use
        v-if="syncDrive"
        ref="sync"
        xlink:href="#sync-extruder"
        transform="translate(278, 385) scale(.030)"
      />
    </transition>

    <use
      v-if="action == ACTION_CUTTING_FILAMENT"
      ref="cut"
      xlink:href="#sissors"
      class="cut1-effect"
    />
    <use
      v-if="action == ACTION_CUTTING_TIP"
      ref="cutTip"
      xlink:href="#sissors"
      class="cut2-effect"
    />

    <use
      v-if="action == ACTION_PURGING"
      ref="poop"
      xlink:href="#blob"
      class="blob-effect"
      :stroke="lowerNozzleColor"
      :fill="lowerNozzleColor"
    />
  </svg>
</template>

<script lang="ts">
import { Component, Mixins, Prop, Ref, Watch } from 'vue-property-decorator'
import StateMixin from '@/mixins/state'
import MmuMixin from '@/mixins/mmu'

@Component({})
export default class MmuFilamentStatus extends Mixins(StateMixin, MmuMixin) {
  @Prop({ default: 0.7 })
  readonly animationTime!: number

  @Ref('filamentRect')
  readonly filamentRect?: SVGElement

  private filamentRectHeight: number = 0
  private tipFormingClass: string = ''

  readonly POSITIONS = {
    unknown: 8,
    'before-pre-gate': 20,
    'pre-gate': 25,
    'after-pre-gate': 40,
    'before-gear': 50,
    gear: 55,
    'after-gear': 70,
    gate: 85,
    'after-gate': 100,
    encoder: 115,
    'start-bowden': 135,
    'mid-bowden': 221,
    'end-bowden': 290,
    extruder: 295,
    'extruder-entrance': 308,
    'before-toolhead': 315,
    toolhead: 325,
    'cooling-tube': 338,
    'cut-point': 355,
    'nozzle-start': 371,
  } as const

  readonly BOWDEN_RANGE = 173 as const

  @Watch('$typedState.printer.printer.mmu.bowden_progress')
  onBowdenProgress (): void {
    // Percentage movement in the bowden
    this.calcFilamentHeight(this.filamentPos)
  }

  @Watch('$typedState.printer.printer.mmu.filament_pos')
  onFilamentPosChanged (newPos: number): void {
    // Filament position state
    this.calcFilamentHeight(newPos)
  }

  @Watch('$typedState.printer.printer.mmu.sensors')
  onSensorsChanged (): void {
    // Update on sensor change
    this.calcFilamentHeight(this.filamentPos)
  }

  @Watch('$typedState.printer.printer.mmu.action')
  onActionChanged (action: string): void {
    // Action being performed
    if (action === this.ACTION_FORMING_TIP) {
      this.tipFormingClass = 'form-tip-effect'
    } else {
      if (this.tipFormingClass) {
        this.$nextTick(() => {
          this.animateFilament(this.POSITIONS['cooling-tube'], 1)
        })
      }
      this.tipFormingClass = ''
    }
  }

  private calcFilamentHeight (filamentPos: number): void {
    let pos = 0

    if (this.gate === this.TOOL_GATE_BYPASS) {
      // Bypass use case places more emphasis on sensors
      switch (filamentPos) {
        case this.FILAMENT_POS_EXTRUDER_ENTRY:
          pos = this.POSITIONS['before-toolhead']
          break

        case this.FILAMENT_POS_HOMED_TS:
          pos = this.POSITIONS['toolhead']
          break

        case this.FILAMENT_POS_IN_EXTRUDER:
          pos = this.POSITIONS['cooling-tube']
          break

        case this.FILAMENT_POS_LOADED:
          pos = this.POSITIONS['nozzle-start']
          break

        default:
          // For everything else, rely on sensors
          if (this.isSensorTriggered('toolhead')) {
            pos = this.POSITIONS['toolhead']
          } else if (this.isSensorTriggered('extruder')) {
            pos = this.POSITIONS['extruder']
          } else if (this.isSensorTriggered('mmu_gear')) {
            pos = this.POSITIONS['after-gear']
          } else if (this.isSensorTriggered('mmu_pre_gate')) {
            pos = this.POSITIONS['after-pre-gate']
          } else {
            pos = this.POSITIONS['before-pre-gate']
          }
          break
      }
    } else {
      // Normal MMU use case leveraging state machine
      switch (filamentPos) {
        case this.FILAMENT_POS_UNLOADED:
          if (this.isSensorTriggered('mmu_gear')) {
            pos = this.POSITIONS['after-gear']
          } else if (this.isSensorTriggered('mmu_pre_gate')) {
            pos = this.POSITIONS['after-pre-gate']
          } else {
            pos = this.POSITIONS['before-pre-gate']
          }
          break

        case this.FILAMENT_POS_HOMED_GATE:
          if (this.configGateHomingEndstop === 'mmu_gear') {
            pos = this.POSITIONS['gear']
          } else if (this.configGateHomingEndstop === 'mmu_gate') {
            pos = this.POSITIONS['gate']
          } else if (this.configGateHomingEndstop === 'extruder') {
            pos = this.POSITIONS['extruder'] // Special no-bowden case
          } else {
            pos = this.POSITIONS['after-gate']
          }
          break

          // TODO: State not yet implmented in Happy Hare
          // case this.FILAMENT_POS_HOMED_ENCODER:
          //    pos = this.POSITIONS['encoder']
          //    break

        case this.FILAMENT_POS_START_BOWDEN:
          if (this.bowdenProgress >= 0) {
            pos = this.POSITIONS['start-bowden'] + (this.BOWDEN_RANGE * this.bowdenProgress) / 100
          } else {
            pos = this.POSITIONS['start-bowden']
          }
          break

        case this.FILAMENT_POS_IN_BOWDEN:
          if (this.bowdenProgress >= 0) {
            pos = this.POSITIONS['start-bowden'] + (this.BOWDEN_RANGE * this.bowdenProgress) / 100
          } else {
            pos = this.POSITIONS['mid-bowden']
          }
          break

        case this.FILAMENT_POS_END_BOWDEN:
          if (
            this.configGateHomingEndstop === 'none' ||
                        (this.hasSensor('toolhead') &&
                            this.isSensorEnabled('toolhead') &&
                            !this.configExtruderForceHoming)
          ) {
            // No extruder homing will be performed so indicate at the extruder now
            pos = this.POSITIONS['extruder-entrance']
          } else {
            pos = this.POSITIONS['end-bowden']
          }
          break

        case this.FILAMENT_POS_HOMED_ENTRY:
          pos = this.POSITIONS['extruder']
          break

        case this.FILAMENT_POS_HOMED_EXTRUDER:
          pos = this.POSITIONS['extruder-entrance']
          break

        case this.FILAMENT_POS_EXTRUDER_ENTRY:
          pos = this.POSITIONS['before-toolhead']
          break

        case this.FILAMENT_POS_HOMED_TS:
          pos = this.POSITIONS['toolhead']
          break

        case this.FILAMENT_POS_IN_EXTRUDER:
          pos = this.POSITIONS['cooling-tube']
          break

        case this.FILAMENT_POS_LOADED:
          pos = this.POSITIONS['nozzle-start']
          break

        default: // this.FILAMENT_POS_UNKNOWN
          pos = this.POSITIONS['unknown']
      }
    }
    this.animateFilament(pos)
  }

  private animateFilament (newHeight: number, animationTime: number = this.animationTime) {
    const rect = this.filamentRect
    if (rect) {
      if (animationTime > 0) {
        const currentHeight = parseFloat(getComputedStyle(rect).height) ?? this.POSITIONS['end-bowden']
        const difference = Math.abs(currentHeight - newHeight)
        const duration = Math.min((difference / this.BOWDEN_RANGE) * animationTime + 0.1, animationTime)
        rect.style.transition = `height ${duration}s ease-in`
      } else {
        rect.style.transition = 'none'
      }
    }
    this.filamentRectHeight = newHeight
  }

  get encoderPosText (): string {
    if (this.encoderPos < 10000) return `${this.encoderPos} mm`
    return `${this.encoderPos}`
  }

  get temperatureClass (): string {
    const canExtrude = this.$typedState.printer.printer.extruder?.can_extrude ?? false
    if (canExtrude === false) return 'text-disabled'
    return ''
  }

  get temperatureText (): string {
    const extTemp = this.$typedState.printer.printer.extruder?.temperature ?? null
    if (extTemp) return `${extTemp.toFixed(0)}°C`
    return ''
  }

  private hasSensor (sensorName: string): boolean {
    return sensorName in this.sensors
  }

  private isSensorEnabled (sensorName: string): boolean {
    return this.sensors[sensorName] != null
  }

  private isSensorTriggered (sensorName: string): boolean {
    const value = this.sensors[sensorName]
    return value !== null && value === true
  }

  sensorClass (sensorName: string): string {
    let sClass = ''
    if (!this.isSensorEnabled(sensorName)) {
      sClass = sensorName === 'extruder' ? 'sensor-disabled-extruder' : 'sensor-disabled'
    } else {
      sClass = this.isSensorTriggered(sensorName)
        ? 'sensor-triggered'
        : sensorName === 'extruder'
          ? 'sensor-open-extruder'
          : 'sensor-open'
    }
    return this.$vuetify.theme.dark ? sClass + '-dark-theme' : sClass + '-light-theme'
  }

  get encoderClass (): string {
    let eClass = ''
    // TODO: Need to separate encoder runout disable from general availability (like other sensors)
    if (this.filamentPos === this.FILAMENT_POS_UNLOADED) eClass = 'sensor-disabled'
    eClass = this.encoderPos ? 'sensor-triggered' : 'sensor-open'
    return this.$vuetify.theme.dark ? eClass + '-dark-theme' : eClass + '-light-theme'
  }

  get hasSyncFeedback (): boolean {
    return (
      this.syncFeedbackEnabled && (this.hasSensor('filament_compression') || this.hasSensor('filament_tension'))
    )
  }

  get homedToEncoder (): boolean {
    if (this.filamentDirection === this.DIRECTION_LOAD) {
      return this.configGateHomingEndstop === 'encoder' && this.filamentPos === this.FILAMENT_POS_START_BOWDEN
    } else {
      return this.configGateHomingEndstop === 'encoder' && this.filamentPos === this.FILAMENT_POS_START_BOWDEN
    }
  }

  get homedToGear (): boolean {
    return this.configGateHomingEndstop === 'mmu_gear' && this.filamentPos === this.FILAMENT_POS_HOMED_GATE
  }

  get homedToGate (): boolean {
    return this.configGateHomingEndstop === 'mmu_gate' && this.filamentPos === this.FILAMENT_POS_HOMED_GATE
  }

  get homedToExtruder (): boolean {
    return this.filamentPos === this.FILAMENT_POS_HOMED_ENTRY
  }

  get homedToExtruderEntrance (): boolean {
    return this.filamentPos === this.FILAMENT_POS_HOMED_EXTRUDER
  }

  get homedToToolhead (): boolean {
    return this.filamentPos === this.FILAMENT_POS_HOMED_TS
  }

  get upperNozzleFull (): boolean {
    return this.filamentPos === this.FILAMENT_POS_LOADED || !!this.varsFilamentRemaining
  }

  get lowerNozzleFull (): boolean {
    return (
      this.filamentPos === this.FILAMENT_POS_LOADED ||
            !!this.varsFilamentRemaining ||
            !!this.varsFilamentRemainingColor
    )
  }

  get upperNozzleColor (): string {
    if (this.varsFilamentRemaining) return this.varsFilamentRemainingColor
    return 'none'
  }

  get lowerNozzleColor (): string {
    if (this.varsFilamentRemainingColor) return this.varsFilamentRemainingColor
    return this.currentGateColor
  }

  get currentGateColor (): string {
    const color = this.gate === this.TOOL_GATE_BYPASS
      ? this.$typedGetters['spoolman/getActiveSpool']?.filament.color_hex ?? null
      : this.$typedState.printer.printer.mmu?.gate_color[this.gate] ?? ''

    return this.fromColorString(color)
  }

  mounted () {
    this.calcFilamentHeight(this.filamentPos)
  }
}
</script>

<style scoped>
.svg-colors {
  --background-light-theme: #ffffff;
  --zone-background-light-theme: #f0f0f0;
  --background-dark-theme: #262629;
  --zone-background-dark-theme: #2c2c2c;
  --disabled-stroke: #808080;
  --color-outline:#2CA9BC;
}

svg text {
    fill: currentColor;
}

.text-disabled {
    opacity: 0.5;
}

.zone-background-light-theme {
    fill: var(--zone-background-light-theme);
}

.sensor-triggered-light-theme {
    fill: limegreen;
}

.sensor-disabled-light-theme {
    stroke: var(--disabled-stroke);
    stroke-dasharray: 2, 1;
    fill: var(--zone-background-light-theme);
}

.sensor-open-light-theme {
    fill: var(--zone-background-light-theme);
}

.sensor-disabled-extruder-light-theme {
    stroke: var(--disabled-stroke);
    stroke-dasharray: 2, 1;
    fill: var(--background-light-theme);
}

.sensor-open-extruder-light-theme {
    fill: var(--background-light-theme);
}

.zone-background-dark-theme {
    fill: var(--zone-background-dark-theme);
}

.sensor-triggered-dark-theme {
    fill: limegreen;
}

.sensor-disabled-dark-theme {
    stroke: var(--disabled-stroke);
    stroke-dasharray: 2, 1;
    fill: var(--zone-background-dark-theme);
}

.sensor-open-dark-theme {
    fill: var(--zone-background-dark-theme);
}

.sensor-disabled-extruder-dark-theme {
    stroke: var(--disabled-stroke);
    stroke-dasharray: 2, 1;
    fill: var(--background-dark-theme);
}

.sensor-open-extruder-dark-theme {
    fill: var(--background-dark-theme);
}

.tool-text {
    font-size: 28px;
    font-weight: bold;
}

.tool-bypass {
    font-size: 16px;
    font-weight: normal;
}

.fade-enter-active,
.fade-leave-active {
    transition: opacity 0.8s ease;
}

.fade-enter,
.fade-leave-to {
    opacity: 0;
}

@keyframes fadeInOut {
    0%,
    100% {
        opacity: 0;
        transform: translate(250px, 414px) scale(1);
    }
    90% {
        opacity: 1;
        transform: translate(250px, 414px) scale(16);
    }
}
.blob-effect {
    animation: fadeInOut 4s infinite;
}

@keyframes cut1 {
    0%,
    100% {
        opacity: 0.5;
        transform: translate(190px, 145px) scale(1.2);
    }
    30%,
    70% {
        opacity: 1;
        transform: translate(205px, 145px) scale(1.2);
    }
}
.cut1-effect {
    animation: cut1 3s infinite;
}

@keyframes cut2 {
    0%,
    100% {
        opacity: 0.5;
        transform: translate(190px, 365px) scale(1.2);
    }
    30%,
    70% {
        opacity: 1;
        transform: translate(205px, 365px) scale(1.2);
    }
}
.cut2-effect {
    animation: cut2 3s infinite;
}

@keyframes form-tip {
    0%,
    100% {
        height: 371px;
    }
    50% {
        height: 338px;
    }
}
.form-tip-effect {
    animation: form-tip 1s infinite;
}
</style>
